package gspringdi

import (
	"fmt"
	"gitee.com/hongzhaomin/hzm-common-go/beautify"
	"gitee.com/hongzhaomin/hzm-common-go/easycmd"
	"gitee.com/hongzhaomin/hzm-common-go/easylog"
	"go/ast"
	"go/parser"
	"go/token"
	"golang.org/x/mod/modfile"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

var _ easycmd.Handler = (*ComponentScanner)(nil)

type ComponentScanner struct {
	ScanRootDir             string `argName:"srd" usage:"扫描根路径" initVal:"./"`
	root                    string
	isAbs                   bool
	injectableStructs       []string
	pkgName2ImportSuffixMap map[string]string
	modName                 string
}

func (my *ComponentScanner) DoHandle(_ *easycmd.Command) {
	my.pkgName2ImportSuffixMap = make(map[string]string)
	my.genCode(my.ScanRootDir)
}

// //go:generate go run gspring_init_generate.go
func (my *ComponentScanner) genCode(root string) {
	my.root = root
	my.isAbs = filepath.IsAbs(root)
	// 遍历项目目录
	fset := token.NewFileSet()

	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			easylog.Error("文件扫描异常：%v", err)
			return nil
		}

		if strings.HasSuffix(path, "go.mod") {
			// 解析 go.mod 文件
			// 获取模块名称
			my.modName = my.parseModPath(path)
			return nil
		}

		if info.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
			return nil
		}

		// 解析单个文件
		file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
		if err != nil {
			easylog.Error("解析文件 %s 失败: %v", path, err)
			return nil
		}

		isNeedParse := false
		for _, importSpec := range file.Imports {
			if importSpec.Path.Value == "\"gitee.com/hongzhaomin/gspring/annotation\"" {
				isNeedParse = true
				break
			}
		}
		if !isNeedParse {
			return nil
		}

		// 提取带注解的结构体
		my.parseGspringBean(file, path)
		return nil
	})

	if err != nil {
		easylog.Error("文件扫描异常：%v", err)
	}

	// 生成代码
	my.genRegisterCode()
}

func (my *ComponentScanner) parseGspringBean(file *ast.File, path string) {
	ast.Inspect(file, func(n ast.Node) bool {
		// 检查是否为结构体定义
		typeSpec, ok := n.(*ast.TypeSpec)
		if !ok {
			return true
		}

		// 检查是否为结构体类型
		structType, isStruct := typeSpec.Type.(*ast.StructType)
		if !isStruct {
			return true
		}

		for _, field := range structType.Fields.List {
			if len(field.Names) == 0 {
				if my.theSameStruct(field, "Component") ||
					my.theSameStruct(field, "Configuration") ||
					my.theSameStruct(field, "ScanStructField") {
					packageName := file.Name.Name
					if packageName == "main" {
						packageName = ""
					}
					if packageName != "" {
						if _, exist := my.pkgName2ImportSuffixMap[packageName]; !exist {
							dir, _ := filepath.Split(path)
							importSuffix := ""
							if my.isAbs {
								for _, s := range strings.Split(strings.ReplaceAll(dir, my.root, ""), string(filepath.Separator)) {
									if s != "" {
										importSuffix += "/" + s
									}
								}
								my.pkgName2ImportSuffixMap[packageName] = importSuffix
							} else {
								for _, s := range strings.Split(dir, string(filepath.Separator)) {
									if s != "" {
										importSuffix += "/" + s
									}
								}
								my.pkgName2ImportSuffixMap[packageName] = importSuffix
							}
						}
						packageName += "."
					}
					my.injectableStructs = append(my.injectableStructs, packageName+typeSpec.Name.Name)
					break
				}
			}
		}
		return true
	})
}

func (my *ComponentScanner) theSameStruct(field *ast.Field, targetStructName string) bool {
	structType, ok := field.Type.(*ast.SelectorExpr)
	if !ok {
		return false // Not a selector expression, not a field from another struct/package
	}
	_, ok = structType.X.(*ast.Ident) // Check if the selector's X is an identifier (package name or alias)
	if !ok {
		return false // Not an identifier, not a direct reference to a struct name or alias in the same package
	}
	return structType.Sel.Name == targetStructName // Compare with the target struct name or alias in the same package context
}

func (my *ComponentScanner) parseModPath(goModPath string) string {
	// 读取 go.mod 文件内容
	data, err := os.ReadFile(goModPath)
	if err != nil {
		easylog.Error("failed to read go.mod: %v", err)
	}

	// 解析 go.mod 文件
	mf, err := modfile.Parse("go.mod", data, nil)
	if err != nil {
		easylog.Error("failed to parse go.mod: %v", err)
	}

	return mf.Module.Mod.Path

	// 输出模块路径和Go版本信息
	//fmt.Printf("Module: %s\n", mf.Module.Mod.Path)
	//fmt.Printf("Go version: %s\n", mf.Go.Version)
	//
	//// 遍历依赖项
	//for _, req := range mf.Require {
	//	fmt.Printf("Require: %s@%s\n", req.Mod.Path, req.Mod.Version)
	//}
}

func (my *ComponentScanner) genRegisterCode() {
	tmpl := `package {{.PackageName}}

import (
	"gitee.com/hongzhaomin/gspring"{{range .Imports}}
	"{{.}}"{{end}}
)

func init() {
{{range .Structs}}
	gspring.Register({{.}}{}){{end}}
}
`
	var imports []string
	for _, importSuffix := range my.pkgName2ImportSuffixMap {
		imports = append(imports, my.modName+importSuffix)
	}

	data := struct {
		PackageName string
		Imports     []string
		Structs     []string
	}{
		PackageName: "main", // 假设生成到 main 包
		Imports:     imports,
		Structs:     my.injectableStructs,
	}

	fmt.Println(beautify.FmtJson(imports))

	file, err := os.Create("gspring_init.go")
	if err != nil {
		easylog.Error("代码文件[gspring_init.go]创建失败：%v", err)
	}
	defer func(file *os.File) {
		err = file.Close()
		if err != nil {
			easylog.Error("文件关闭异常：%v", err)
		}
	}(file)

	t := template.Must(template.New("di").Parse(tmpl))
	if err = t.Execute(file, data); err != nil {
		easylog.Error("代码文件模板填充异常: %v", err)
	}
}
